;*********************************************************************
; ZEICHENAUSGABE MIT LCD HD44780
; Author: Ottmar
; Date:   2025.04.24
;
; Der mitgeteilte Code beruht weitgehendst auf der Quelle
;        www.sprut.de\electronic\pic\programm\lcd.zip\
; Dies ist eine Zusammenfassung mit dem Schwerpunkt auf
; LCD-Adressierung und LCD-Zeichenausgabe.
;*********************************************************************
;
; INFO: Unterschiedliche LCD's haben unterschiedliche Zeilenadressen.
;
;--DDRAM-ADDRESSES (1st Digit in the line) depending on LCD_Type
   LINE1        EQU   0x00  ;all LCDs
   LINE2        EQU   0x40  ;all LCDs
   ;
   IF LCD_TYPE == 1         ;LCD_2x8 (evtl auch 1x16)
     LCD_LINES   EQU   .2 
     LCD_DIGITS  EQU   .8
   ENDIF
   ;
   IF LCD_TYPE == 2         ;LCD_2x16
     LCD_LINES   EQU   .2 
     LCD_DIGITS  EQU   .18
   ENDIF
   ;
   IF LCD_TYPE==3
     LINE3      EQU   0x10  ;LCDs 4x16
     LINE4      EQU   0x50
     LCD_LINES   EQU   d'4'
     LCD_DIGITS  EQU   .16
   ENDIF
   ;
   IF LCD_TYPE==4
     LINE3      EQU   0x14  ;LCDs 4x20
     LINE4      EQU   0x54
     LCD_LINES   EQU   d'4'
     LCD_DIGITS  EQU   d'20'
   ENDIF
   ;
   SET_DDRAM_ADDR EQU   0x80  ;Function "setze DDRAM-ADDRESS"
   SET_CGRAM_ADDR EQU   0x40  ;"        "setze CGRAM Address"
   ;LCD-Function (RS=1)
   ;movlw   0x40              ;z/B. Zeile 2
   ;xorlw   SET_DDRAM_ADDR    ;Adresse mit Funktion verknuepfen
   ; 
;CALL Lcd_Init
   ;
; Ist das LCD einmal initialisiert, kann ein Zeichen uebertragen werden.
; Im Beispiel soll die Zahl "210" soll rechtsbuending in Zeile 1, 
; LCD 2x16 geschrieben werden.
;-----------------------------
; Dezimalstellen  ..43210  Info
; Digit: 0123456789012345	16 Digits/Zeile
;                     210  schreiben ab Digit 12
;------------------------------
; NUR ZAHLEN: Vor der Ausgabe muss eine Zahl z.b. "210" in ihre 
; einzelnen Dezimalstellen 2,1,0 zerlegt worden sein und im RAM z.B
; als BCD2:0 verfuegbar sein um einzeln ins LCD geschrieben zu werden.
; ZEICHEN ALLGEMEIN : Uebergabe des Zeichens als ASCII z.B. so:
; movlw  "A" ..."Z" "0"..."9" 
   ;
   movlw	"2"		   ;Asciiwert Zahl ins WREG
   movwf	BCD2	      ;in Variable speichern
   movlw	"1"
   movwf	BCD1
   movlw	"0"
   movwf	BCD0
   ;
;1. AUSGABEADRESSE IN DAS DDRAM SCHREIBEN
	movlw LINE1+d'11'       ;Ausgabeadresse Digit 11 im DDRAM des LCD
	CALL  OutDDRAM_Addr     ;siehe weiter unten
	;
;2. ZEICHEN AUSGEBEN
	movlw	BCD2			      ;"2"
	CALL  OutLcd_Data       ;Adresszaehler intern +1
	movlw BCD1              ;"1"
	CALL  OutLcd_Data       ;Adresszaehler intern +1
	movlw BCD0              ;"0"
	CALL  OutLcd_Data       ;Adresszaehler am Ende der Zeile
;	------
;	Fertig!
;  ===================================================================
; BUSYFLAG AUSLESEN
; LcdBusy kann durch Delay mindestens 1.52ms ersetzt werden, indem
; RETURN/HOME LCD-intern 1,52m Verarbeitunszeit benoetigt. Der RW-Pin 
; des LCD kann in diesem Fall auf GND gelegt werden.
; Siehe dazu Hitachi HD44780U Datenblatt, Table 6 Instructions.
;
; Hierdurch koennen die kuerzeren Verarbeitungszeiten von z.B.
; Write Data, Set_DDRAM_ADDRESS  von jeweils = 37us+4us jedoch nicht
; ausgenutzt werden. Je Adresse und Zeichen ist das Das Delay 2x
; also 4x erforderlich 4x1.5=6ms Zeitbedarf. Allerdings wird bei
; fortlaufender Zeichenausgabe der Adresszaehler intern erhoeht
;  -------------------------------------------------------------------
;
;  ...wie es weitergehen koennte:
;
;	Mit FSR/INDF lassen sich mehr Zeichen/Zeilen einfach ausgeben, wenn
;	diese aus einer Tabelle (LUT = Look Up Table) ausgelesen werden.
;	Die Texte koennen rasch und funktionsbezogen geaendert werden.
;
;	LUT-BEISPIEL (angedeutet mit FSR (File Select Register)
;  Das Beispiel soll nur die prinzipielle Vorgehensweise darstellen!
;
tbl_menu:     ;Einsprung-Label
              ;LCD z.B. 20 Digits
              ;01234567890123456789,Endmarke    
   dt LINE1+0,"Bttn: [1]  [2]  [3] ",80h  ;Tasterbezeichnung
	dt LINE2+0,"Func: STOP BACK FRWD",80h  ;Text ab Digit0
	dt LINE3+5,"V0.1"               ,80h   ;Text ab Digit 5
	;
; Ausgabe der Tabelle:
; movlw  LOW tbl_menue     ;
; movwf  cntPntr0          ;Pointer auf Lowbyte Tabellenanfang
; movlw  .2                ;2 Zeilen ausgeben nicht groesser als LINES!
; CALL   Lcd_LUT
; -------------
;Lcd_LUT:                  ;Subroutine
;  movwf cntLines          ;Zaehler Anzahl auszugebender Zeilen
;  movfw cntPntr0          ;Adresse 1.Zeichen in der LUT
;  movwf FSR0
;  movf  INDF0,w
;  movwf Lcd_Addr          
;  CALL  OutDDRAM_Addr     ;1. Zeichen in Zeile ist Adresse
;loop:                     ;Schleifenbeginn Zeichenausgabe
;  incf  FSR0,f
;  movf  INDF0,w           ;LUT-Wert ins WREG
;  addlw 80h               ;Zeilenende? 80h+80h =d'256 -> 255->0
;  btfsc STATUS,Z          ;Z=1? Zeilenende erreicht?
;  GOTO  next line         ;JA
;  movf  INDF0,w           ;NRIN, LUT-Wert nochmals ins WREG
;  CALL  OutLcd_Data       ;Zeichen ausgeben
;  decfsz cntLines,f       ;Zeilenaehler -1 = 0? Z=1?
;  GOTO  loop              ;NEIN naechste Zeile ausgeben
;  RETURN                  ;JA,fertig!
;
;*********************************************************************
;  SUBROUTINES
;*********************************************************************
OutDDRAM_Addr:
	iorlw   SET_DDRAM_ADDR  ;Adresse mit Funktionsbefehl s.o. verknpfen
	;
OutLcd_Ctrl:
; sends 1 instruction-byte through the LCD 4bit-interface (2 nibbles)
; remeber: READ from PORT, WRITE to LAT-Register
; toggelt Hi-Nibble und dann Low=Nibble in das LCD-Interface
   movwf   LcdCtrl         ;copy Instruction from WREG to variable
   CALL    LcdBusy         ;check LCD ready for next instruction
;Anstatt LcdBusy kann auch auch ein Delay1ms verwendet werden 
   movf    LcdCtrl,w       ;contents instruction (e.g. LCD-adress)
   ANDLW   b'11110000'      ;Bitmask to select HI-nibble 
   BANKSEL  LCD_LAT         ;alternate to bank2
   movwf   LCD_LAT         ;LCD_PORT write Nibble to 4bit-bus
   bsf     LCD_E          ;LCD_LAT,E=1 toggle Enable
   nop
   bcf     LCD_E          ;LCD_LAT,E =0 toggle
   BANKSEL  0
   SWAPF   LcdCtrl, w      ;change the nibbles
   ANDLW   b'11110000'      ;Bitmask to select the LO-nibble
   BANKSEL  LCD_LAT
   movwf   LCD_LAT         ;LATx/LCD_PORTT write Nibble to 4bit-bus
   bsf     LCD_E          ;toggle
   nop
   bcf     LCD_E          ;Control-bus is now disabled
   BANKSEL   0            ;bank 0
   RETURN
;*********************************************************************
OutLcd_Data:	
;sends 1 data-byte through the LCD 4bit-interface (2 nibbles)
   movwf    LcdData           ;copy Instruction from WREG to variable
   CALL     LcdBusy           ;LCD ready to receive next instruction?
   movf     LcdData,w         ;contents LCD-Data (Charakter)
   ANDLW    b'11110000'       ;Bitmask to select HI-nibble
   BANKSEL  LCD_LAT           ;Wechsel zu Bank 2
   movwf    LCD_LAT           ;LCD_PORT write Nibble to 4bit-databus
   bsf      LCD_RS            ;1=LCD-Data-mode
   bsf      LCD_E             ;enable LCD 4bit-databus
   nop                        ;toggle
   bcf      LCD_E             ;disable databus
   BANKSEL  0
   SWAPF    LcdData, w        ;change the nibbles
   ANDLW    b'11110000'       ;Bitmask to select the LO-nibble
   BANKSEL  LCD_LAT
   movwf    LCD_LAT           ;LATx /LCD_PORT write Nibble to 4bit-bus
   bsf      LCD_RS            ;1=LCD-Data-mode
   bsf      LCD_E             ;enable LCD 4bit-databus
   nop                        ;toggle
   bcf      LCD_E             ;databus is now disabled   
   bcf      LCD_RS            ;=0 LCD-control-mode
   BANKSEL  0                 ;bank 0
   RETURN
	
	
	
	
	
	
	
	
	
	
	
	